import qs from 'qs';
import {HistoryUtils, iChangeListener, iHistoryConfig, iHistoryHandler, iHistoryMeta} from "./history.utils";

export function createHistoryCreator(handler: iHistoryHandler) {
  return ({ base }: iHistoryConfig) => {

    if (!base) {base = '/';}
    if (base.charAt(0) !== '/') {base = `/${base}`;}

    const utils = {
      getMeta: (url: string): iHistoryMeta => {
        url = handler.getPath(url);
        if (url.charAt(0) !== '/') {url = `/${url}`;}
        if (url.indexOf(base) === 0) {url = url.slice(base.length);}
        return HistoryUtils.decodePath(url);
      }
    };

    const historyState = { route: utils.getMeta(HistoryUtils.getLocationMeta().fullPath), };

    const methods = {
      push: (path: string, query?: Record<string, any>, hash?: string) => {
        let url = path;
        if (!!query) {
          url += '?' + qs.stringify(query);
        }
        if (!!hash) {
          url += '#' + hash;
        }
        handler.push(HistoryUtils.pathJoin(base, url));
      },
      back: () => {window.history.back();},
      onPathChange: (() => {

        let listeners: iChangeListener[] = [];

        const oldPushState = window.history.pushState;

        function newPushState(state: any, title: string, path: string) {
          const meta = utils.getMeta(path);
          if (meta.fullPath === historyState.route.fullPath) {return; }
          historyState.route = meta;
          listeners.forEach(fn => fn(meta));
          return oldPushState.apply(window.history, [state, title, path]);
        }

        window.history.pushState = newPushState;

        window.addEventListener('popstate', () => {
          const meta = utils.getMeta(HistoryUtils.getLocationMeta().fullPath);
          historyState.route = meta;
          listeners.forEach(fn => fn(meta));
        });

        return (listener: iChangeListener) => {
          listeners.push(listener);
          return () => {
            const index = listeners.indexOf(listener);
            index > -1 && listeners.splice(index, 1);
          };
        };
      })(),
    };

    return {
      state: historyState,
      ...methods,
    };
  };
}

export const createHashHistory = createHistoryCreator(({
  getPath: (path: string) => {
    const separatorIndex = path.indexOf('#');
    if (separatorIndex === -1) {
      return "";
    }
    return path.slice(separatorIndex + 1);
  },
  push: (path) => {
    const url = new URL(window.location.href);
    url.hash = path;
    window.history.pushState({}, '', `${url.pathname}${url.search}${url.hash}`);
  },
}));

export const createBrowserHistory = createHistoryCreator(({
  getPath: (path: string) => {
    return path;
  },
  push: (path) => {
    window.history.pushState({}, '', path);
  },
}));
